package com.whfc.mach.manager.impl;

import com.whfc.common.enums.BindFlag;
import com.whfc.common.enums.DelFlag;
import com.whfc.common.enums.NetState;
import com.whfc.common.enums.WorkState;
import com.whfc.common.util.MathUtil;
import com.whfc.mach.dao.*;
import com.whfc.mach.dto.*;
import com.whfc.mach.entity.AppMachData;
import com.whfc.mach.entity.AppMachDevice;
import com.whfc.mach.entity.AppMachDeviceLog;
import com.whfc.mach.manager.MachMgr;
import com.whfc.mach.manager.MachStrategyMgr;
import com.whfc.mach.manager.strategy.oil.OilStrategy;
import com.whfc.mach.redis.MachOilSheetRedisDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @author : qzexing
 * @version : 1.0
 * @date : 2021-02-20 15:56
 */
@Service
public class MachMgrImpl implements MachMgr {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private AppMachMapper appMachMapper;

    @Autowired
    private AppMachDeviceMapper appMachDeviceMapper;

    @Autowired
    private AppMachDeviceLogMapper appMachDeviceLogMapper;

    @Autowired
    private AppMachOilSheetMapper appMachOilSheetMapper;

    @Autowired
    private MachOilSheetRedisDao machOilSheetRedisDao;

    @Autowired
    private AppMachDataMapper appMachDataMapper;

    @Autowired
    private MachStrategyMgr machStrategyMgr;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bind(Integer machId, Integer deviceId, Integer userId, String userName) {
        Date now = new Date();

        //添加设备-硬件关系
        AppMachDevice machDevice = new AppMachDevice();
        machDevice.setMachId(machId);
        machDevice.setDeviceId(deviceId);
        machDevice.setBindTime(now);
        machDevice.setBindUser(userName);
        appMachDeviceMapper.insertSelective(machDevice);

        //增加绑定记录
        AppMachDeviceLog machDeviceLog = new AppMachDeviceLog();
        machDeviceLog.setMachId(machId);
        machDeviceLog.setDeviceId(deviceId);
        machDeviceLog.setTime(now);
        machDeviceLog.setUserId(userId);
        machDeviceLog.setUserName(userName);
        machDeviceLog.setType(BindFlag.BIND.getValue());
        appMachDeviceLogMapper.insertSelective(machDeviceLog);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unbind(Integer machId, Integer userId, String userName) {
        // 根据machId 查找绑定信息
        AppMachDevice appMachDevice = appMachDeviceMapper.selectByMachId(machId);
        if (appMachDevice != null) {
            Integer deviceId = appMachDevice.getDeviceId();
            //删除绑定关系
            appMachDeviceMapper.deleteByMachId(machId);

            //增加解绑记录
            AppMachDeviceLog machDeviceLog = new AppMachDeviceLog();
            machDeviceLog.setMachId(machId);
            machDeviceLog.setDeviceId(deviceId);
            machDeviceLog.setTime(new Date());
            machDeviceLog.setType(BindFlag.UNBIND.getValue());
            machDeviceLog.setUserId(userId);
            machDeviceLog.setUserName(userName);
            appMachDeviceLogMapper.insertSelective(machDeviceLog);

            //更新app_mach中的綁定信息
            appMachMapper.updateBindFlagByMachId(machId, BindFlag.UNBIND.getValue(), null, null, null);

            // 清除app_mach_data 中的数据
            this.clearMachData(machId);
        }
    }

    @Override
    public BoardMachNumDTO getBoardMachNumDTO(List<AppBoardMachDTO> machList, Set<String> machTypeNameSet) {
        List<AppMachTypeNumDTO> machTypeNumList = new ArrayList<>();
        BoardMachNumDTO boardMachNumDTO = new BoardMachNumDTO();
        for (String machTypeName : machTypeNameSet) {
            AppMachTypeNumDTO appMachTypeNumDTO = new AppMachTypeNumDTO();
            appMachTypeNumDTO.setMachTypeName(machTypeName);
            int idleNum = 0;
            int workNum = 0;
            int stopNum = 0;
            int offlineNum = 0;
            int num = 0;
            for (AppBoardMachDTO appMachDTO : machList) {
                Integer workState = appMachDTO.getWorkState();
                Integer netState = appMachDTO.getNetState();
                if (machTypeName.equals(appMachDTO.getMachTypeName())) {
                    num++;
                    if (com.whfc.common.enums.NetState.OFFLINE.getValue().equals(netState)) {
                        offlineNum++;
                    } else {
                        if (com.whfc.common.enums.WorkState.IDLE.getValue().equals(workState)) {
                            idleNum++;
                        }
                        if (com.whfc.common.enums.WorkState.RUN.getValue().equals(workState)) {
                            workNum++;
                        }
                        if (com.whfc.common.enums.WorkState.STOP.getValue().equals(workState)) {
                            stopNum++;
                        }
                    }
                }
            }
            appMachTypeNumDTO.setIdleNum(idleNum);
            appMachTypeNumDTO.setWorkNum(workNum);
            appMachTypeNumDTO.setStopNum(stopNum);
            appMachTypeNumDTO.setOfflineNum(offlineNum);
            appMachTypeNumDTO.setNum(num);
            machTypeNumList.add(appMachTypeNumDTO);
        }

        Integer idleNum = 0;
        Integer workNum = 0;
        Integer stopNum = 0;
        Integer offlineNum = 0;
        Integer num = 0;
        for (AppMachTypeNumDTO appMachTypeNumDTO : machTypeNumList) {
            idleNum += appMachTypeNumDTO.getIdleNum();
            workNum += appMachTypeNumDTO.getWorkNum();
            stopNum += appMachTypeNumDTO.getStopNum();
            offlineNum += appMachTypeNumDTO.getOfflineNum();
            num += appMachTypeNumDTO.getNum();
        }

        boardMachNumDTO.setIdleNum(idleNum);
        boardMachNumDTO.setWorkNum(workNum);
        boardMachNumDTO.setStopNum(stopNum);
        boardMachNumDTO.setOfflineNum(offlineNum);
        boardMachNumDTO.setNum(num);
        boardMachNumDTO.setAppMachTypeNumList(machTypeNumList);
        return boardMachNumDTO;
    }

    @Override
    public Oil computeRemainOil(Integer machId, Integer oilPos) {
        AppMachInfo machInfo = appMachDeviceMapper.selectAppMachInfoByMachId(machId);
        return computeRemainOil(machInfo, oilPos);
    }

    @Override
    public Oil computeRemainOil(AppMachInfo machInfo, Integer oilPos) {
        Double oilHeight = machInfo.getOilTankHeight();
        Double oilVolume = machInfo.getOilTankVolume();
        OilStrategy oilStrategy = machStrategyMgr.getOilStrategy(machInfo.getHoldDeptId(), machInfo.getMachType());
        AppMachOilLookup lookup = this.getOilLookup(machInfo.getMachId(), oilHeight, oilVolume);
        return oilStrategy.computeRemainOil(oilPos, oilHeight, oilVolume, lookup);
    }

    @Override
    public List<Oil> computeRemainOil(Integer machId, List<Oil> oilList) {
        AppMachInfo machInfo = appMachDeviceMapper.selectAppMachInfoByMachId(machId);
        return this.computeRemainOil(machInfo, oilList);
    }

    @Override
    public List<Oil> computeRemainOil(AppMachInfo machInfo, List<Oil> oilList) {
        Double oilHeight = machInfo.getOilTankHeight();
        Double oilVolume = machInfo.getOilTankVolume();
        OilStrategy oilStrategy = machStrategyMgr.getOilStrategy(machInfo.getHoldDeptId(), machInfo.getMachType());
        AppMachOilLookup lookup = this.getOilLookup(machInfo.getMachId(), oilHeight, oilVolume);
        return oilStrategy.computeRemainOil(oilList, oilHeight, oilVolume, lookup);
    }

    @Override
    public AppMachOilLookup getOilLookup(Integer machId, Double oilHeight, Double oilVolume) {

        //获取油箱标定数据
        List<AppMachOilSheetDTO> oilSheetDTOList = machOilSheetRedisDao.get(machId);
        if (oilSheetDTOList == null) {
            oilSheetDTOList = appMachOilSheetMapper.selectByMachId(machId);
            machOilSheetRedisDao.set(machId, oilSheetDTOList);
        }

        //数据转换
        AppMachOilLookup lookup = new AppMachOilLookup();
        if (oilSheetDTOList.size() > 0) {
            int size = oilSheetDTOList.size();
            AppMachOilSheetDTO low = oilSheetDTOList.get(0);
            AppMachOilSheetDTO high = oilSheetDTOList.get(size - 1);
            Integer maxPos = Math.max((int) (oilHeight * 10), high.getOilPos());
            Integer minPos = 0;
            Double maxVolume = oilVolume;
            Double minVolume = 0D;
            int len = maxPos;
            Map<Integer, Double> oilSheetMap = new HashMap<>(len + 1);
            for (int pos = minPos; pos <= maxPos; pos++) {
                Double remainOil = null;
                //[0,low]
                if (pos <= low.getOilPos()) {
                    double rate = (low.getOilVolume() - minVolume) / (low.getOilPos() - minPos);
                    remainOil = rate * (pos - minPos) + minVolume;
                }
                //[high,max]
                else if (pos >= high.getOilPos()) {
                    double rate = (maxVolume - high.getOilVolume()) / (maxPos - high.getOilPos());
                    remainOil = rate * (pos - high.getOilPos()) + high.getOilVolume();
                }
                //(low,high)
                else {
                    for (int i = 0; i < size; i++) {
                        AppMachOilSheetDTO current = oilSheetDTOList.get(i);
                        //刚好相等
                        if (current.getOilPos().equals(pos)) {
                            remainOil = current.getOilVolume();
                            break;
                        }
                        //第一个大于当前油位的点
                        if (current.getOilPos() > pos) {
                            AppMachOilSheetDTO l = oilSheetDTOList.get(i - 1);
                            AppMachOilSheetDTO h = oilSheetDTOList.get(i);
                            int lPos = l.getOilPos();
                            int hPos = h.getOilPos();
                            double rate = (h.getOilVolume() - l.getOilVolume()) / (hPos - lPos);
                            remainOil = l.getOilVolume() + (pos - lPos) * rate;
                            break;
                        }
                    }
                }
                if (remainOil != null) {
                    oilSheetMap.put(pos, MathUtil.round(remainOil, 2));
                }
            }
            lookup.setMinPos(minPos);
            lookup.setMaxPos(maxPos);
            lookup.setMinVolume(low.getOilVolume());
            lookup.setMaxVolume(high.getOilVolume());
            lookup.setOilSheetMap(oilSheetMap);
        }
        return lookup;
    }

    @Override
    public void refreshOilSheet(Integer machId, List<AppMachOilSheetDTO> oilSheetDTOS) {
        if (oilSheetDTOS != null && oilSheetDTOS.size() > 0) {
            machOilSheetRedisDao.delete(machId);
            appMachOilSheetMapper.deleteByMachId(machId);
            appMachOilSheetMapper.batchInsert(machId, oilSheetDTOS);
        }
    }

    @Override
    public List<AppMachOilSheetDTO> getOilSheet(Integer machId) {
        //获取油箱标定数据
        List<AppMachOilSheetDTO> oilSheetDTOList = appMachOilSheetMapper.selectByMachId(machId);
        machOilSheetRedisDao.set(machId, oilSheetDTOList);
        return oilSheetDTOList;
    }

    /**
     * 清除设备数据
     *
     * @param machId 设备ID
     */
    private void clearMachData(Integer machId) {
        appMachDataMapper.deleteByMachId(machId);

        AppMachData record = new AppMachData();
        record.setMachId(machId);
        record.setTime(new Date());
        record.setNetState(NetState.OFFLINE.getValue());
        record.setDelFlag(DelFlag.UNDELETE.getValue());
        record.setWorkState(WorkState.STOP.getValue());
        record.setTotalLength1(0L);
        record.setTotalLength2(0L);
        record.setTotalWorkHours(0D);
        record.setTotalOilWear(0D);
        appMachDataMapper.insertOrUpdate(record);
    }
}
